package com.gitee.huanminabc.db_freedom.wrapper;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.gitee.huanminabc.jcommon.str.StringUtil;
import com.gitee.huanminabc.db_freedom.base.SqlFun;
import com.gitee.huanminabc.jcommon.reflect.LambdaUtil;
import com.gitee.huanminabc.jcommon.reflect.FieldUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;

@EqualsAndHashCode(callSuper = true)
@Data
public abstract class AbstractSqlWrapper<T, Children extends AbstractSqlWrapper<T, Children>> extends AbstractBaseWrapper<T, Children> {
    protected StringBuilder caseWhen = new StringBuilder();
    //操作记录
    protected List<String> operationRecord=new ArrayList<>();
    //这种是为了支持多表查询
    private final Map<String,TableInfo> entityClassList=new HashMap<>();
    protected final Children typedThis = (Children) this; //对象本身
    //全局别名索引 , 这样就能保证不同的子查询别名不会重复

    private  int aliasIndex=1;//别名索引
    public  static  String prefixAlias="t";

    public   String getNewAlias(){
        return prefixAlias+aliasIndex++;
    }

    private T entity;
    private  TableInfo tableInfo;

    public T getEntity() {
        return entity;
    }
    @Override
    public MergeSegments getExpression() {
        return null;
    }

    @Override
    public void clear() {
        caseWhen=new StringBuilder();
        SQL().set(new SQL());
        entityClassList.clear();
        operationRecord.clear();
        aliasIndex=1;

    }

    @Override
    public String getSqlSegment() {
        return  SQL().toString();
    }


    public AbstractSqlWrapper() {}

    public AbstractSqlWrapper(Class<?> entityClass, SQL sql) {
        super(sql);
        try {
           this.entity= (T)entityClass.newInstance();
            this.tableInfo= createTableInfo(entityClass);
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }

    }

    protected TableInfo createTableInfo(Class<?> entityClass){
        TableInfo tableInfo = new TableInfo();
        String tableName = classToTableName(entityClass);
        tableInfo.setEntityClass(entityClass);
        tableInfo.setTableName(tableName);
        tableInfo.setAlias(getNewAlias());
        entityClassList.put(entityClass.getName(),tableInfo);
        return tableInfo;
    }


    protected  void addTable(TableInfo tableInfo){
        entityClassList.put(tableInfo.getEntityClass().getName(),tableInfo);
    }
    protected  void addTables(Map<String,TableInfo> tableInfoMap){
        entityClassList.putAll(tableInfoMap);
    }


    //通过Class获取表名
    protected String classToTableName(Class<?> clazz){
        //识别注解上的@TableName
        TableName annotation = clazz.getAnnotation(TableName.class);
        //如果没有注解则使用类名
        if (annotation != null) {
            //获取表名
            return annotation.value();
        }else{
            String simpleName = clazz.getSimpleName();
            //首字母小写 Aa -> aa
            String tableName = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
            //将驼峰转为下划线 aBc -> a_bc
            tableName = StringUtil.toUnderline(tableName);
            return tableName;
        }
    }

    //加param
    public Children V(Object param){
        SQL().append(" ? ");
        SQL().addParam(param);
        return typedThis;
    }

    protected  List<String> getColumnAll(){
        List<String> list = new ArrayList<>();
        //获取当前类的所有字段
        Class<?> entityClass = getTableInfo().getEntityClass();
        //只获取可访问的方法
        Method[] declaredMethods = entityClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            //跳过Object的方法
            if(declaredMethod.getDeclaringClass().equals(Object.class)){
                continue;
            }
            String name = declaredMethod.getName();
            //排除注解上是忽略的@TableField(exist = false)
            TableField annotation = declaredMethod.getAnnotation(TableField.class);
            if(annotation!=null&& !annotation.exist()){
                continue;
            }
            //判断方法是get set is
            if(name.startsWith("get")||name.startsWith("is")){
                String property = FieldUtil.methodToField(name);
                list.add(property);
            }
        }
        return list;
    }



    //处理lambda表达式字段
    protected  <Y> void getFunFieldName(SqlFun<Y, ?> column, Consumer<SQL.SqlColumn> consumer){
        getFunFieldName(column,consumer,true);
    }
    protected  <Y> void getFunFieldName(SqlFun<Y, ?> column, Consumer<SQL.SqlColumn> consumer, boolean isExist){
        Map<String, Object> stringObjectMap = LambdaUtil.lambdaInvocation(column);
        String field = stringObjectMap.get("fieldName").toString();
        Class<?> entityClass = (Class<?>) stringObjectMap.get("clazz");
        //因为基于mybaitis plus的原因,还需要判断字段上有TableField注解那么就取这个注解的值
        //这个注解是为了支持数据库字段和实体类字段不一致的情况

        Field field1 = FieldUtil.getField(entityClass, field);
        TableField annotation = field1.getAnnotation(TableField.class);
        if(annotation!=null&& StringUtil.isNotEmpty(annotation.value())){
            field=annotation.value();
        }
        //判断是不是当前类
        if(entityClass.equals(getTableInfo().getEntityClass())) {
            consumer.accept(new SQL.SqlColumn(getTableInfo().getAlias(),field));
        }else{
            TableInfo tableInfo = getEntityClassList().get(entityClass.getName());
            //如果是空那么就表示没有加入此类表,那么就加入
            if(tableInfo==null&&isExist){
                tableInfo = this.createTableInfo(entityClass);
            }else if(tableInfo==null){
               throw new RuntimeException(entityClass.getName()+"没有加入到语句中,请检查语法是否正确");
            }
            consumer.accept(new SQL.SqlColumn(tableInfo.getAlias(),field));
        }
    }

    @Override
    public String toString() {
        return  SQL().toString();
    }
}
